package
{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	
	public class ParentPolygon extends Polygon
	{
		
		public static const MIN_SIDE_PROP:Number = .3, MAX_SIDE_PROP:Number = .7;
		public static const MIN_ANGLE_DEGREES:Number = 10, MAX_ANGLE_DEGREES:Number = 170;
		// public objects	
		public var PARENT_WIDTH:Number, PARENT_HEIGHT:Number; // keep record of original height and with of parent object
		public var id:String;
		public var used:Boolean = false;
		public var draw_as:String;
		public var bounce:Boolean = false; // when set to true will change direction on next frame
		public var WIDTH:Number, HEIGHT:Number, CUR_WIDTH:Number, CUR_HEIGHT:Number, ACTUAL_WIDTH:Number, ACTUAL_HEIGHT:Number;
		public var MIN_AREA:Number, MIN_DIAG_LENGTH:Number, MAX_DIAG_LENGTH:Number, MIN_SIDE_LENGTH:Number, MAX_SIDE_LENGTH:Number; // begins as a proportion but is immediately changed to an absolute distance
		public var concavities:uint = 0;
		// private objects
		private var targetXML:XML; // the xml that tells this object what kind of shape he is
		private var TYPES:Array; // The possible types of shapes
		private var shape:Sprite;
		private var face:Sprite;
		private var eyeL:Eye, eyeR:Eye;
		private var nose:Shape;
		private var mouth:Mouth;
		private var _neighbors:Array; // an array of polygons to use for hit testing
		// private vars
		private var _scaleX:Number=1, _scaleY:Number=1, _scale:Number=1;
		private var _color:uint, lightColor:uint, darkColor:uint;
		// at fps blink once about every 10 seconds
		private var fps:Number, frameCount:int=0, randomJerkMod:int;		
		// for moving
		private var maxSpeedMultiplier:Number, minSpeedMultiplier:Number, movePerFrame:Number, currentMoveAngle:Number, moveRadius:Number;
		private var moveCenterPoint:Point;
		private var _moving:Boolean=false;
		public function set scaleWidth(_scale:Number):void
		{
			this.WIDTH *= _scale;
			this._scaleX = _scale;
			this._scale = Math.min(this._scale, _scaleX);
			drawShapeFromPoints();
			this.scaleX = _scale;
		}
		public function set scaleHeight(_scale:Number):void
		{
			this.HEIGHT *= _scale;
			this._scaleY = _scale;
			this._scale = Math.min(this._scale, _scaleY);
			drawShapeFromPoints();
			this.scaleY = _scale;
		}
		public function set scale(_scale:Number):void
		{
			this.HEIGHT *= _scale;
			this.WIDTH *= _scale;
			this._scaleX = _scale;
			this._scaleY = _scale;
			this._scale = _scale;
			drawShapeFromPoints();
			this.scaleX = _scale;
			this.scaleY = _scale;
		}
		
		override public function get color():uint{return _color;}
		override public function set color(u:uint):void{_color=u; drawShapeFromPoints();}
		
		public function get neighbors():Array{return _neighbors;}
		public function set neighbors(arr:Array):void
		{
			// go through neighbors and make sure that none are this
			_neighbors = new Array();
			for each (var n:Polygon in arr)
			{
				if (n != this) _neighbors.push(n);
			}
		}
		public function get moving():Boolean{return (_moving);}
		public function set moving(b:Boolean):void
		{
			if (b)
			{
				this.moveRandomly(this.width);
			} else
			{
				this.stopMoving();
			}
		}
		
		/** Initiates drawing of a "parent" shape.  If points is given draw the shape contained within, should also contain an internal color
		 * If ponts is not given draw a random shape that fits the defining features of the shape
		 */
		public function ParentPolygon(WIDTH:Number, HEIGHT:Number, type:String, id:String, TYPES:Array, points:Array = null, color:uint = 0x000000, draw_as:String="")
		{
			super (null,0,0);
			this.mouseEnabled = false;
			this.points = points;
			if (points != null)
			{
				this.WIDTH = MathUtilities.findBoundingWidth(points);
				this.HEIGHT = MathUtilities.findBoundingHeight(points);
			} else
			{
				this.WIDTH = WIDTH;
				this.HEIGHT = HEIGHT;
			
			}
			this.CUR_WIDTH = WIDTH;
			this.CUR_HEIGHT = HEIGHT;
			this.MIN_SIDE_LENGTH = MIN_SIDE_PROP * Math.min(WIDTH, HEIGHT);
			this.MAX_SIDE_LENGTH = MAX_SIDE_PROP * Math.min(WIDTH, HEIGHT);
			this.MIN_DIAG_LENGTH = MIN_SIDE_LENGTH;
			this.MAX_DIAG_LENGTH = Math.min(WIDTH, HEIGHT);
			this.MIN_AREA = HEIGHT/3 * WIDTH/3;
			this.targetXML = targetXML;
			this.id = id;
			this.TYPES = TYPES;
			// figure out what type this is
			this.type = type;
			this.draw_as = draw_as;
			//trace(this.type, this.draw_as);
			this._color = color;
			if (TYPES == null || TYPES.indexOf(type) > -1)
			{				
				if (this.draw_as == "" || TYPES.indexOf(draw_as) == -1)
				{
					this.draw_as = type;
				}
				// get random shape
				if (points == null || points.length == 0)
				{
					// get random background
					drawRandomShape();							
				} // draw the given shape 
				else
				{
					drawShapeFromPoints();
				}
				this.graphics.lineStyle(0, 0xFFFFFF,0);
				this.graphics.drawRect(0,0,WIDTH,HEIGHT);
				
				this.addEventListener(Event.ADDED, handleAdded);
			} else
			{
				trace("This is not an acceptable type of shape");
			}
		}   
		
		private function handleAdded(event:Event):void
		{
			trace("add parent", event.currentTarget, event.target);
			if (event.currentTarget == event.target)// && event.currentTarget == this)
			{
				this.removeEventListener(Event.ADDED, handleAdded);
				fps = ShapeProductionTask.fps;
				PARENT_HEIGHT = parent.height;
				PARENT_WIDTH = parent.width;
				this.addEventListener(Event.ENTER_FRAME, handleFrame);
				this.addEventListener(Event.REMOVED, handleRemoved);
			}
		}
			
		/** Draws a shape based on the given points */
		private function drawShapeFromPoints():void
		{
			if (this.numChildren > 0)
			{
				for (var i:int=0; i < this.numChildren; i++) 
				{ 
					this.removeChild(this.getChildAt(i));
				}
			}
			if (color == 0x000000) _color = Math.ceil(0xAAAAAA * Math.random()) + 0x333333;
			shape = new Sprite();	
			this.addChild(shape);
			shape.graphics.lineStyle(1,0x888888);
			shape.graphics.beginFill(color);
			
			var init:Boolean = false;
			for each (var p:Point in points)
			{
				//trace("parent point", type, id, p.x, p.y)
				if (!init){ shape.graphics.moveTo(p.x, p.y); init=true}
				else { shape.graphics.lineTo(p.x, p.y); }
			}		
			shape.graphics.lineTo(points[0].x, points[0].y);
			shape.graphics.endFill();
			var r:int, g:int, b:int;
			// get dark color
			r = color & 0xFF0000; r = r >> 16;
			g = color & 0x00FF00; g = g >> 8;
			b = color & 0x0000FF;
			var dr:int = r-r/4; if (dr < 0) dr = 0;
			var dg:int = g-g/4; if (dg < 0) dg = 0;
			var db:int = b-b/4; if (db < 0) db = 0;
			darkColor = dr << 16 | dg << 8 | db;
			// get light color
			var lr:int = r+r/4; if (lr > 255) lr = 255;
			var lg:int = g+g/4; if (lg > 255) lg = 255;
			var lb:int = b+b/4; if (lb > 255) lb = 255;
			lightColor = lr << 16 | lg << 8 | lb;
			
			//var s:Number = Math.min(1, MathUtilities.area(points)/(WIDTH/2*HEIGHT/2));
			var s:Number = 1;
			if (points != null)
			{
				var maxDim:Number = Math.max(WIDTH,HEIGHT);
				s = ShapeProductionTask.unit*_scale*MathUtilities.area(points)/(maxDim*maxDim);
				//if (points.length==3) s=s/2;
			} 
			//trace(_scaleX, _scaleY, MathUtilities.area(points), (WIDTH*5/8*HEIGHT*5/8), s);
			// place light face
			
			face = new Sprite();
			//face.graphics.beginFill(lightColor);
			face.graphics.beginFill(0xFFFFFF, .1);
			face.graphics.drawRoundRect(centroid.x-20*s, centroid.y-34*s, 40*s, 54*s, 50*s, 30*s);
			face.graphics.endFill();
			this.addChild(face);
			// draw two eyes on the shape;
			var irisColor:uint = Math.ceil(0xAAAAAA * Math.random()) + 0x333333;
			//var darkColor:uint = (color & 0xFEFEFE) >> 1;
			eyeL = new Eye(15*s, 20*s, color, darkColor, irisColor, Eye.LEFT);
			eyeR = new Eye(15*s, 20*s, color, darkColor, irisColor, Eye.RIGHT);
			eyeL.x = centroid.x - 10*s;
			eyeL.y = centroid.y - 20*s;
			eyeR.x = centroid.x + 10*s;
			eyeR.y = centroid.y - 20*s;
			this.addChild(eyeL);
			this.addChild(eyeR);
			nose = new Shape();
			nose.graphics.lineStyle(3*s, darkColor);
			nose.graphics.beginFill(color);
			nose.graphics.drawEllipse(-8*s,-8*s,16*s,16*s);
			nose.graphics.endFill();
			nose.graphics.lineStyle(3*s, color);
			nose.graphics.beginFill(color);
			nose.graphics.drawRect(-8*s, -8*s, 16*s, 8*s);
			nose.graphics.endFill();
			
			nose.x = centroid.x;
			nose.y = centroid.y;
			this.addChild(nose);
			mouth = new Mouth(30*s, 15*s, darkColor);
			mouth.x = centroid.x;
			mouth.y = centroid.y + 12*s;
			this.addChild(mouth);
		}
		/** Goes through a huge switch of possible shapes and draws one */
		private function drawRandomShape():void
		{
			var p:Point, p1:Point, p2:Point, p3:Point, p4:Point;
			var i:int, start:int, count:int;
			var d:Number, d1:Number, d2:Number, ang:Number, ori:Number;
			var which1:uint, which2:uint; // for cases where we have some decisions to make
			var otherConditions:Boolean;
			var npoints:uint;
			var typeSplit:Array = draw_as.split("_");
			//trace(draw_as);
			switch (typeSplit[typeSplit.length-1])
			{
				case "dodecagon": npoints=12; break;
				case "decagon": npoints=10; break;
				case "nonagon": case "enneagon": npoints=9; break;
				case "octagon": npoints=8; break;
				case "heptagon": npoints=7; break;
				case "hexagon": npoints=6; break;
				case "pentagon": npoints=5; break;
				case "quadrilateral": case "quadrangle": npoints=4; break;
				case "triangle": npoints=3; break;
			}
			switch (draw_as)
			{
				case "dodecagon": case "decagon": case "nonagon": case "enneagon": case "octagon": case "heptagon": case "hexagon": case "pentagon": case "quadrilateral": case "quadrangle": case "triangle":
					do 
					{
						//always start with a new array
						points = new Array();
						for (i=0; i < npoints; i++)
						{
							p = new Point(Math.floor(Math.random()*WIDTH), Math.floor(Math.random()*HEIGHT));
							points.push(p);
						}
						// these points need to be ordered correctly
						points = MathUtilities.orderPointsPolygon(points);
					} while (points == null || !checkConditions());
					break;
				case "concave_dodecagon": case "concave_decagon": case "concave_nonagon": case "concave_enneagon": case "concave_octagon": case "concave_heptagon": case "concave_hexagon": case "concave_pentagon": case "concave_quadrilateral": case "concave_quadrangle":
					do 
					{
						//always start with a new array
						points = new Array();
						for (i=0; i < npoints; i++)
						{
							p = new Point(Math.floor(Math.random()*WIDTH), Math.floor(Math.random()*HEIGHT));
							points.push(p);
						}
						// these points need to be ordered correctly
						points = MathUtilities.orderPointsPolygon(points);
					} while (points == null || !checkConditions(false, true));
					break;
				case "convex_dodecagon": case "convex_decagon": case "convex_nonagon": case "convex_enneagon": case "convex_octagon": case "convex_heptagon": case "convex_hexagon": case "convex_pentagon": case "convex_quadrilateral": case "convex_quadrangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();
						for (i=0; i < npoints; i++)
						{
							p = new Point(Math.floor(Math.random()*WIDTH), Math.floor(Math.random()*HEIGHT));
							points.push(p);
						}
						// these points need to be ordered correctly
						points = MathUtilities.orderPointsPolygon(points);
					} while (!checkConditions(true));
					break;
				case "trapezoid":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						 // fourth point is determined by parallel lines
						which1 = Math.round(Math.random()*1); // which sides
						if (which1)
						{
							p1 = points[2]; p2 = points[1]; p3 = points[0];
						} else
						{
							p1 = points[0]; p2 = points[1]; p3 = points[2]
						}
						// if which1 is 0 then we are connecting to the first point in parallel, else we are aligning with point2 in parallel
						ang = MathUtilities.calcAngle(p1, p2);
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						p = new Point(p3.x+d*Math.sin(ang), p3.y-d*Math.cos(ang));
						points.push(p);
					} while (!checkConditions(true));
					break;
				case "isosceles_trapezoid":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1]; p3 = points[2];
						ang = MathUtilities.calcAcuteAngleBetween(p3, p1, p2);
						ori = MathUtilities.calcAngle(p3, p2) + ang;
						d1 = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						d2 = MathUtilities.distance(p3.x, p3.y, p2.x, p2.y);
						if (ang > Math.PI/2){	ang -= Math.PI/2; d=d2*Math.sin(ang); d = d1 + 2*d }
						else {ang = Math.PI/2 - ang; d=d2*Math.sin(ang); d = d1 - 2*d }
						
						p = new Point(p3.x+d*Math.sin(ori), p3.y-d*Math.cos(ori));
						points.push(p);
					} while (!checkConditions(true));
					break;
				case "right_trapezoid":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1]; p3 = points[2];
						ang = MathUtilities.calcAcuteAngleBetween(p3, p1, p2);
						ori = MathUtilities.calcAngle(p3, p2) + ang;
						d1 = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						d2 = MathUtilities.distance(p3.x, p3.y, p2.x, p2.y);
						if (ang > Math.PI/2){	ang -= Math.PI/2; d=d2*Math.sin(ang); d = d1 + d }
						else {ang = Math.PI/2 - ang; d=d2*Math.sin(ang); d = d1 - d }
						
						p = new Point(p3.x+d*Math.sin(ori), p3.y-d*Math.cos(ori));
						points.push(p);
					} while (!checkConditions(true));
					break;
				case "kite":
					do {
						points = new Array();	
						// random point in one half
						i = Math.floor(Math.random()*2);
						p1 = new Point(Math.floor(Math.random()*WIDTH), Math.floor(Math.random()*HEIGHT/2+HEIGHT/2*i));
						points.push(p1);
						// random point on the clockwise side
						p2 = new Point(Math.floor(Math.random()*WIDTH/2+WIDTH/2*(i+1)%2), Math.floor(Math.random()*HEIGHT));
						points.push(p2);
						d1 = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						d2 = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1);
						ang = Math.random() * Math.PI/2;
						p3 = new Point(p2.x+d2*Math.sin(ori+ang), p2.y-d2*Math.cos(ori+ang));
						points.push(p3);
						ori = MathUtilities.calcAngle(p3, p2);
						// get diagonal
						d = MathUtilities.distance(p1.x, p1.y, p3.x, p3.y);
						// use law of sines to get half the angle
						ang = Math.PI - 2*MathUtilities.lawOfSines(d, Math.PI - ang, d1, 0); 
						p4 = new Point(p3.x+d2*Math.sin(ori+ang), p3.y-d2*Math.cos(ori+ang));
						points.push(p4);
					} while (!checkConditions(true));
					break;
				case "parallelogram":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1]; p3 = points[2];
						ang = MathUtilities.calcAcuteAngleBetween(p3, p1, p2);
						ori = MathUtilities.calcAngle(p3, p2) + ang;
						d = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						p = new Point(p3.x+d*Math.sin(ori), p3.y-d*Math.cos(ori));
						points.push(p);
					} while (!checkConditions(true));
					break;
				case "rhombus":
					do {
						points = new Array();	
						// random point in one half
						i = Math.floor(Math.random()*2);
						p1 = new Point(Math.floor(Math.random()*WIDTH), Math.floor(Math.random()*HEIGHT/2+HEIGHT/2*i));
						points.push(p1);
						// random point on the clockwise side
						p2 = new Point(Math.floor(Math.random()*WIDTH/2+WIDTH/2*(i+1)%2), Math.floor(Math.random()*HEIGHT));
						points.push(p2);
						d1 = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						ori = MathUtilities.calcAngle(p2, p1);
						ang = Math.random() * Math.PI/2;
						p3 = new Point(p2.x+d1*Math.sin(ori+ang), p2.y-d1*Math.cos(ori+ang));
						points.push(p3);
						ori = MathUtilities.calcAngle(p3, p2);
						ang = Math.PI - ang;
						p4 = new Point(p3.x+d1*Math.sin(ori+ang), p3.y-d1*Math.cos(ori+ang));
						points.push(p4);
					} while (!checkConditions(true));
					break;
				case "rectangle":
				case "isosceles_right_trapezoid":
					do {
						points = new Array();						
						// two random point in a random quadrant
						i = Math.floor(Math.random()*4);
						p1 = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
						points.push(p1);
						i = (i+1)%4;
						p2 = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
						points.push(p2);
						d1 = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						d2 = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1);
						ang = Math.PI/2;
						p3 = new Point(p2.x+d2*Math.sin(ori+ang), p2.y-d2*Math.cos(ori+ang));
						points.push(p3);
						ori = MathUtilities.calcAngle(p3, p2);
						p4 = new Point(p3.x+d1*Math.sin(ori+ang), p3.y-d1*Math.cos(ori+ang));
						points.push(p4);
					} while (!checkConditions(true));
					break;
				case "regular_quadrilateral":
				case "regular_quadrangle":
				case "square":
					do {
						points = new Array();						
						// two random point in a random quadrant
						i = Math.floor(Math.random()*4);
						p1 = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
						points.push(p1);
						i = (i+1)%4;
						p2 = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
						points.push(p2);
						d = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
						ang = Math.PI/2;
						while (points.length < 4)
						{
							ori = MathUtilities.calcAngle(p2, p1);
							p3 = new Point(p2.x+d*Math.sin(ori+ang), p2.y-d*Math.cos(ori+ang));
							points.push(p3);
							p1 = p2;
							p2 = p3;
						}
					} while (!checkConditions(true));
					break;
				/*
				case "triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
					} while (!checkConditions(true));
					break;
				*/
				case "scalene_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 3)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						if (Math.min(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2]))/Math.max(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2])) > .9 || Math.min(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0]))/Math.max(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0])) > .9 || Math.min(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1]))/Math.max(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1])) > .9)
						{
							otherConditions = false;
						} else
						{
							otherConditions = true;
						}
					} while (!checkConditions(true) || !otherConditions);
					break;
				case "right_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);						
					} while (!checkConditions(true));
					break;
				case "scalene_right_triangle":
				case "right_scalene_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);			
						if (Math.min(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2]))/Math.max(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2])) > .9 || Math.min(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0]))/Math.max(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0])) > .9 || Math.min(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1]))/Math.max(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1])) > .9)
						{
							otherConditions = false;
						} else
						{
							otherConditions = true;
						}
					} while (!checkConditions(true));
					break;
				case "obtuse_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.random()*Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);						
					} while (!checkConditions(true));
					break;
				case "obtuse_scalene_triangle":
				case "scalene_obtuse_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.random()*Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
						if (Math.min(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2]))/Math.max(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2])) > .9 || Math.min(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0]))/Math.max(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0])) > .9 || Math.min(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1]))/Math.max(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1])) > .9)
						{
							otherConditions = false;
						} else
						{
							otherConditions = true;
						}
					} while (!checkConditions(true));
					break;
				case "acute_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.PI - Math.random()*Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);						
					} while (!checkConditions(true));
					break;
				case "scalene_acute_triangle":
				case "acute_scalene_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = Math.random() * (this.MAX_SIDE_LENGTH - this.MIN_SIDE_LENGTH) + this.MIN_SIDE_LENGTH; 
						ori = MathUtilities.calcAngle(p2, p1) + Math.PI - Math.random()*Math.PI/2;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);
						if (Math.min(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2]))/Math.max(MathUtilities.distanceP(points[0], points[1]), MathUtilities.distanceP(points[1], points[2])) > .9 || Math.min(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0]))/Math.max(MathUtilities.distanceP(points[1], points[2]), MathUtilities.distanceP(points[2], points[0])) > .9 || Math.min(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1]))/Math.max(MathUtilities.distanceP(points[2], points[0]), MathUtilities.distanceP(points[0], points[1])) > .9)
						{
							otherConditions = false;
						} else
						{
							otherConditions = true;
						}
					} while (!checkConditions(true) || !otherConditions);
					break;
				case "isosceles_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = MathUtilities.distance(p1.x,p1.y,p2.x,p2.y);
						ang = Math.random() * Math.PI;
						ori = MathUtilities.calcAngle(p2, p1) + ang;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
					} while (!checkConditions(true));
					break;
				case "isosceles_right_triangle":
				case "right_isosceles_triangle":
					do {
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = MathUtilities.distance(p1.x,p1.y,p2.x,p2.y);
						ang = Math.PI/2;
						ori = MathUtilities.calcAngle(p2, p1) + ang;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
					} while (!checkConditions(true));
					break;
				case "isosceles_obtuse_triangle":
				case "obtuse_isosceles_triangle":
					do {
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = MathUtilities.distance(p1.x,p1.y,p2.x,p2.y);
						ang = Math.random()*Math.PI/2;
						ori = MathUtilities.calcAngle(p2, p1) + ang;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
					} while (!checkConditions(true));
					break;
				case "isosceles_acute_triangle":
				case "acute_isosceles_triangle":
					do {
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first three points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = MathUtilities.distance(p1.x,p1.y,p2.x,p2.y);
						ang = Math.PI - Math.random()*Math.PI/2;
						ori = MathUtilities.calcAngle(p2, p1) + ang;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
					} while (!checkConditions(true));
					break;
				case "equilateral_triangle":
				case "regular_triangle":
					// DRAW A RANODOM POINT IN ALL FOUR QUADRANTS								
					do 
					{
						//always start with a new array
						points = new Array();						
						// loop through four quadrants starting with random quadrant
						i = Math.floor(Math.random()*4);
						while (points.length < 2)
						{
							// first two points are random
							p = new Point(Math.floor(Math.random()*WIDTH/2)+WIDTH/2*Math.ceil((i%3)/2), Math.floor(Math.random()*HEIGHT/2)+HEIGHT/2*Math.floor(i/2));
							points.push(p);
							i = (i+1)%4;
						}
						p1 = points[0]; p2 = points[1];
						d = MathUtilities.distance(p1.x,p1.y,p2.x,p2.y);
						ang = Math.PI/2 + Math.PI/6;
						ori = MathUtilities.calcAngle(p2, p1) + ang;
						p3 = new Point(p2.x+d*Math.sin(ori), p2.y-d*Math.cos(ori));
						points.push(p3);	
					} while (!checkConditions(true));
					break;
			}
			// center points
			centerPoints();
			// got the points, now draw it
			drawShapeFromPoints();
		}
		
		/** re-adjust points so that they are more or less centered
		 */ 
		private function centerPoints():void
		{
			// mins and maxs
			var minx:Number=Number.MAX_VALUE, miny:Number=Number.MAX_VALUE, maxx:Number=Number.MIN_VALUE, maxy:Number=Number.MIN_VALUE;
			var ipoints:Array;
			
			// get min and max, put points on each corner
			for each (var p:Point in this.points)
			{
				if (p.x < minx) minx = p.x;
				if (p.x > maxx) maxx = p.x;
				if (p.y < miny) miny = p.y;
				if (p.y > maxy) maxy = p.y;
			}
			ACTUAL_WIDTH = (maxx - minx)/2;
			ACTUAL_HEIGHT = (maxy - miny)/2;
			this.WIDTH = MathUtilities.findBoundingWidth(points);
			this.HEIGHT = MathUtilities.findBoundingHeight(points);	
			for each (p in this.points)
			{
				//p.x = p.x - minx;
				//p.y = p.y - miny;
				p.x = (p.x - minx + p.x + WIDTH - maxx)/2;
				p.y = (p.y - miny + p.y + HEIGHT - maxy)/2;
			}
			
		}
		/** This function is used to ensure that the points meet the min and max conditions 
		 */
		private function checkConditions(checkConvexity:Boolean=false, checkConcavity:Boolean=false, checkDiagonals:Boolean=true):Boolean
		{
			if (points == null) return false;
			var p:Point, p1:Point, p2:Point, pc:Point;
			var d1:Number, d2:Number, d3:Number, m:Number;
			// check distances
			for (var i:int=0; i < points.length; i++)
			{
				p1 = points[(i-1+points.length)%points.length]; // use mod to get the last point on the first index
				p2 = points[i];
				var d:Number = MathUtilities.distance(p1.x, p1.y, p2.x, p2.y);
				if (d < MIN_SIDE_LENGTH || d > MAX_SIDE_LENGTH)
				{
					return false;
				}
			}
			// check diagonals (if a quadrilateral)
			if (checkDiagonals)
			{	
				if (points.length == 4) // later change to all even-sided figures
				{
					d1 = MathUtilities.distance(points[0].x, points[0].y, points[2].x, points[2].y);
					d2 = MathUtilities.distance(points[1].x, points[1].y, points[3].x, points[3].y);
					if (d1 < MIN_DIAG_LENGTH || d1 > MAX_DIAG_LENGTH ||	d2 < MIN_DIAG_LENGTH || d2 > MAX_DIAG_LENGTH) return false;	
				} else if (points.length == 3) // later change to all odd-sided figures
				{
					p = MathUtilities.midpointPoint(points[1], points[2]);
					d1 = MathUtilities.distance(points[0].x, points[0].y, p.x, p.y);
					p = MathUtilities.midpointPoint(points[0], points[2]);
					d2 = MathUtilities.distance(points[1].x, points[1].y, p.x, p.y);
					p = MathUtilities.midpointPoint(points[1], points[0]);
					d1 = MathUtilities.distance(points[2].x, points[2].y, p.x, p.y);					
					if (d1 < MIN_DIAG_LENGTH || d1 > MAX_DIAG_LENGTH ||	d2 < MIN_DIAG_LENGTH || d2 > MAX_DIAG_LENGTH||	d3 < MIN_DIAG_LENGTH || d3 > MAX_DIAG_LENGTH) return false;	
				}
			}
			// check angles
			concavities = 0;
			for (i = 0; i < points.length; i++)
			{
				p1 = points[(i-1+points.length)%points.length]; 
				pc = points[i];
				p2 = points[(i+1)%points.length];
				var a:Number = MathUtilities.calcAcuteRotationBetween(p1,p2,pc);
				if (a < MIN_ANGLE_DEGREES || a > MAX_ANGLE_DEGREES)
				{
					return false;
				}
				// check for convexity, if necessary
				if (MathUtilities.calcAcuteDirectionBetween(p1, p2, pc) == -1) concavities++;
			}
			//trace(type, "number of concavities", concavities);
			/// there shouldn't be too many concavities, else this probably has some crazy intersecting lines or what not
			if (concavities > points.length-3) return false;
			// we want a convex shape
			if (checkConvexity)
			{
				if (concavities > 0) return false;
			}
			// we want a concave shape
			if (checkConcavity)
			{
				
				if (concavities < 1) return false;
			}
			
			
			// check area
			if (MathUtilities.area(points) < MIN_AREA) return false;
			
			return true;
		}
		/** Initiates random motion by this figure */
		public function moveRandomly(moveRadius:Number, minSpeedMultiplier:Number = 1.0, maxSpeedMultiplier:Number = 1.0):void
		{
			if (this.parent == null) return;
			if (!_moving)
			{
				_moving = true;
				//this.addEventListener(Event.ENTER_FRAME, handleFrameMove);
			}
			this.moveCenterPoint = new Point(x,y);
			this.moveRadius = moveRadius;
			this.minSpeedMultiplier = minSpeedMultiplier;
			this.maxSpeedMultiplier = maxSpeedMultiplier;
			randomJerkMod = Math.floor(Math.random()*(fps/5));
			movePerFrame =  minSpeedMultiplier + Math.random()*(maxSpeedMultiplier - minSpeedMultiplier) * 20 / fps;
			currentMoveAngle = (Math.random() * Math.PI*2) - Math.PI;
		}
		public function stopMoving():void
		{
			if (moving)
			{
				_moving = false;
				//this.removeEventListener(Event.ENTER_FRAME, handleFrameMove);
			}
		}
		
		/** Used for animation of the face */
		private function handleFrame (event:Event):void
		{			
			frameCount++;
			if (moving)
			{
				// big move
				if (frameCount % (fps*2) == randomJerkMod)
				{
					currentMoveAngle += (Math.random() * Math.PI*2) - Math.PI;
					movePerFrame =  minSpeedMultiplier + Math.random()*(maxSpeedMultiplier - minSpeedMultiplier) * 20 / fps;
				} 
				else if (frameCount % (fps/5) == randomJerkMod)
				{
					this.rotation += Math.random()*2-1; // a little wiggle while you walk
					currentMoveAngle += (Math.random() * Math.PI/90) - Math.PI/180;
				}
				if (true)
				{
					var ox:Number = this.x;
					var oy:Number = this.y;
					var nx:Number = this.x+movePerFrame*Math.sin(currentMoveAngle);
					var ny:Number = this.y-movePerFrame*Math.cos(currentMoveAngle);
					this.x = nx;
					this.y = ny;
					//trace(nx, ny, "compare distance", MathUtilities.distance(nx, ny, moveCenterPoint.x, moveCenterPoint.y), moveRadius, parent.width, parent.height);
					var inBounds:Boolean = true;
					if (MathUtilities.distance(nx, ny, moveCenterPoint.x, moveCenterPoint.y) >= moveRadius) inBounds = false;
					//trace(this.name, this.points);
					var parentmin:Point = parent.localToGlobal(new Point(0,0));
					var parentmax:Point = parent.localToGlobal(new Point(PARENT_WIDTH, PARENT_HEIGHT));
					
					var newAngle:Number;
					for each (var p:Point in this.points)
					{
						var gp:Point = this.localToGlobal(p);
	
						//if (!parent.hitTestPoint(gp.x, gp.y, true)) 
						if (gp.x < parentmin.x || gp.x > parentmax.x || gp.y < parentmin.y || gp.y > parentmax.y)
						{
							inBounds = false; 
							newAngle = currentMoveAngle + Math.PI;
							break;
						}
					}
					if (inBounds)	
					{
						if (neighbors != null)
						{
							for each (var np:Polygon in neighbors)
							{
								//if (np != this && np.pointWithinBounds(this, p))
								if (np != this && this.hitTestPolygon(np))
								{
									//trace("collision", p, np);
									bounce = true;
									// move in opposite direction of shapes centroid
									newAngle = MathUtilities.calcAngle(this.localToGlobal(this.centroid), np.localToGlobal(np.centroid));
									break;
								}
							}
						}
					}
					
					//if (MathUtilities.distance(nx, ny, moveCenterPoint.x, moveCenterPoint.y) >= moveRadius || (nx-this.width/2)<=0 || (ny-this.<=0 || nx >= parent.width || ny >= parent.height)
					if (!inBounds || bounce)				
					{
						bounce = false;
						this.x = ox; this.y = oy;
						//currentMoveAngle = (Math.random() * Math.PI*2) - Math.PI;
						currentMoveAngle = newAngle;
						nx = this.x+2*movePerFrame*Math.sin(currentMoveAngle);
						ny = this.y-2*movePerFrame*Math.cos(currentMoveAngle);
						x = nx;
						y = ny;
					}				
				}
			}
			if (!mouth.blinking)
			{
				// give blinking a 1 in 10*framerate chance of occurring
				if (Math.floor(Math.random()*fps*5) == 0)
				{
					mouth.blink();
				}
			}
			if (!Eye(this.eyeL).blinking)
			{
				// give blinking a 1 in 10*framerate chance of occurring
				if (Math.floor(Math.random()*fps*10) == 0)
				{
					Eye(eyeL).blink();
					eyeR.blink();
				}
			}	
		}
		
	
		
		/** remove frame listener */
		private function handleRemoved (event:Event):void
		{
			if (event.target == event.currentTarget)
			{
				trace("removed", id);
				this.addEventListener(Event.ADDED, handleAdded);
				this.removeEventListener(Event.REMOVED, handleRemoved);
				this.removeEventListener(Event.ENTER_FRAME, handleFrame);
				//if (moving) this.removeEventListener(Event.ENTER_FRAME, handleFrameMove);
			}
		}
		
		
		public static function byte2bin(byte:uint):String 
		{     
			var bin:String = '';         
			for(var i:uint = 0; i < 24; i++) 
			{         
				bin += String((byte & (0x800000 >> i)) >> (23 - i));     
			}
			return bin; 
		}
	}
}